/*!
    \copyright  Copyright (c) 2024 Qualcomm Technologies International, Ltd.
                All Rights Reserved.
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \addtogroup le_bap
    \brief
    @{
*/

#if (defined(INCLUDE_LE_AUDIO_UNICAST) || defined(INCLUDE_LE_AUDIO_BROADCAST))
#include "pacs_audio_contexts.h"
#include "pacs_audio_contexts_private.h"
#include "pacs_config.h"
#include "pacs_utilities.h"

#include <bap_server_prim.h>
#include <panic.h>
#include <logging.h>
#include <gatt_connect.h>
#include <gatt.h>

pacs_audio_contexts_data_t pacsAudioContexts_data;

static pacs_audio_contexts_instance_t *pacsAudioContexts_InstanceGetByCid(gatt_cid_t cid)
{
    pacs_audio_contexts_instance_t *match = NULL;
    pacs_audio_contexts_instance_t *inst = NULL;

    ARRAY_FOREACH(inst, pacsAudioContexts_GetData()->pacs_instances)
    {
        if (inst->cid == cid)
        {
            match = inst;
            break;
        }
    }

    return match;
}

void PacsAudioContexts_DestroyInstanceForCid(gatt_cid_t cid)
{
    pacs_audio_contexts_instance_t *inst = pacsAudioContexts_InstanceGetByCid(cid);

    if (inst != NULL)
    {
        memset(inst, 0, sizeof (pacs_audio_contexts_instance_t));
        inst->cid = INVALID_CID;
    }
}

bool PacsAudioContexts_SetAvailableAudioContext(gatt_cid_t cid, audio_contexts_config_t *audio_contexts)
{
    pacs_audio_contexts_instance_t *inst = pacsAudioContexts_InstanceGetByCid(cid);

    if (inst != NULL)
    {
        inst->available_audio_contexts.sink = audio_contexts->sink;
        inst->available_audio_contexts.source = audio_contexts->source;
        return TRUE;
    }

    return FALSE;
}

bool PacsAudioContexts_UpdateCid(gatt_cid_t old_cid, gatt_cid_t new_cid)
{
    pacs_audio_contexts_instance_t *inst = pacsAudioContexts_InstanceGetByCid(old_cid);

    if (inst != NULL)
    {
        inst->cid = new_cid;
        return TRUE;
    }

    return FALSE;
}

bool PacsAudioContexts_InstanceCreate(gatt_cid_t cid)
{
    pacs_audio_contexts_instance_t *inst = pacsAudioContexts_InstanceGetByCid(cid);

    if (inst == NULL)
    {
        inst = pacsAudioContexts_InstanceGetByCid(INVALID_CID);

        if (inst != NULL)
        {
            inst->cid = cid;
        }
        else
        {
            return FALSE;
        }
    }

    DEBUG_LOG("PacsAudioContexts_InstanceCreate instance %p", inst);

    return TRUE;
}

void PacsAudioContexts_InstanceInit(void)
{
    pacs_audio_contexts_data_t *context_data = pacsAudioContexts_GetData();
    pacs_audio_contexts_instance_t *inst = NULL;

    memset(context_data, 0, sizeof(*context_data));

    ARRAY_FOREACH(inst, context_data->pacs_instances)
    {
        inst->cid = INVALID_CID;
    }
}

void PacsAudioContexts_InstanceReset(void)
{
    pacs_audio_contexts_data_t *context_data = pacsAudioContexts_GetData();
    pacs_audio_contexts_instance_t *inst = NULL;

    ARRAY_FOREACH(inst, context_data->pacs_instances)
    {
        memset(inst, 0, sizeof(*inst));
        inst->cid = INVALID_CID;
    }
}

static inline bool pacsConfigure_AreSourceAudioContextsSupported(void)
{
    return !!SOURCE_SUPPORTED_AUDIO_CONTEXTS;
}

void PacsAudioContexts_UpdateAvailableAudioContexts(audio_contexts_config_t audio_contexts)
{
    DEBUG_LOG("PacsAudioContexts_UpdateAvailableAudioContexts sink=0x%x source=0x%x", audio_contexts.sink, audio_contexts.source);
    PanicFalse((~ALL_AUDIO_CONTEXTS & audio_contexts.sink) == 0);
    PanicFalse((~ALL_AUDIO_CONTEXTS & audio_contexts.source) == 0);

    if(audio_contexts.sink != LeBapPacsUtilities_GetSinkAudioContextAvailability())
    {
        LeBapPacsUtilities_SetSinkAudioContextAvailability(audio_contexts.sink);
    }
    if(pacsConfigure_AreSourceAudioContextsSupported() && 
                audio_contexts.source != LeBapPacsUtilities_GetSourceAudioContextAvailability())
    {
        LeBapPacsUtilities_SetSourceAudioContextAvailability(audio_contexts.source);
    }
    DEBUG_LOG("PacsAudioContexts_UpdateAvailableAudioContexts result sink=0x%x source=0x%x", 
               LeBapPacsUtilities_GetSinkAudioContextAvailability(), LeBapPacsUtilities_GetSourceAudioContextAvailability());
}

void PacsAudioContexts_UpdateSupportedAudioContexts(audio_contexts_config_t audio_contexts)
{
    DEBUG_LOG("PacsAudioContexts_UpdateSupportedAudioContexts sink=0x%x source=0x%x", audio_contexts.sink, audio_contexts.source);
    audio_contexts.sink &= ALL_AUDIO_CONTEXTS;
    audio_contexts.source &= ALL_AUDIO_CONTEXTS;

    LeBapPacsUtilities_SetSinkSupportedAudioContexts(audio_contexts.sink);
    if(pacsConfigure_AreSourceAudioContextsSupported())
    {
        LeBapPacsUtilities_SetSourceSupportedAudioContexts(audio_contexts.source);
    }
    DEBUG_LOG("PacsAudioContexts_UpdateSupportedAudioContexts result sink=0x%x source=0x%x", audio_contexts.sink, audio_contexts.source);
}

bool PacsAudioContexts_GetSelectiveAudioContexts(unsigned cid, audio_contexts_config_t *audio_context)
{
    pacs_audio_contexts_instance_t *inst;

    inst = pacsAudioContexts_InstanceGetByCid(cid);
    if (inst != NULL)
    {
        audio_context->sink = inst->available_audio_contexts.sink;
        audio_context->source = inst->available_audio_contexts.source;
        return TRUE;
    }

    return FALSE;
}

UNITCODESECTION(KEEP)
bool PacsAudioContexts_SetSelectiveAvailableAudioContext(gatt_cid_t cid, uint16 sink_audio_contexts, uint16 source_audio_contexts)
{
    bool status = FALSE;
    pacs_audio_contexts_instance_t *inst;

    if (pacsAudioContexts_IsAudioContextManagementDisabled())
    {
        inst = pacsAudioContexts_InstanceGetByCid(cid);

        if (inst != NULL &&
            BapServerSetSelectiveAvailableAudioContexts(LeBapPacsGetBapHandle(),
                                                        cid,
                                                        sink_audio_contexts,
                                                        source_audio_contexts))
        {
            inst->available_audio_contexts.sink = sink_audio_contexts;
            inst->available_audio_contexts.source = source_audio_contexts;
            status = TRUE;
        }
    }

    DEBUG_LOG("PacsAudioContexts_SetSelectiveAvailableAudioContext cid=0x%x sink=0x%x, source=0x%x, status=%d",
                cid, sink_audio_contexts, source_audio_contexts, status);

    return status;
}

UNITCODESECTION(KEEP)
void PacsAudioContexts_DisableDefaultAudioContextManagement(void)
{
    pacsAudioContexts_GetData()->audio_context_management_disable = TRUE;
    BapServerEnableAvailableAudioContextControl(LeBapPacsGetBapHandle());
}

void PacsAudioContexts_Initialise(void)
{
    PacsAudioContexts_InstanceInit();

    audio_contexts_config_t supported_audio_contexts = { .sink = SINK_SUPPORTED_AUDIO_CONTEXTS, .source = SOURCE_SUPPORTED_AUDIO_CONTEXTS };
    PacsAudioContexts_UpdateSupportedAudioContexts(supported_audio_contexts);

    audio_contexts_config_t available_audio_contexts = { .sink = SINK_AVAILABLE_AUDIO_CONTEXTS, .source = SOURCE_AVAILABLE_AUDIO_CONTEXTS };
    PacsAudioContexts_UpdateAvailableAudioContexts(available_audio_contexts);
}

#endif
